﻿using System;
using System.Threading;
using System.IO;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Linq;

namespace RytmSerca.Ekg
{
    /// <summary>
    /// Czytanie plików
    /// </summary>
    internal class EkgSignalReader
    {
        public const int DefaultSampleLimit = 1 << 14;
        int _sampleLimit = DefaultSampleLimit;

        /// <summary>
        /// Ograniczenie liczby wczytywanych próbek. Ustawić potęgę liczby 2. Próbki powyżej najbliższej potęgi 2 będą ignorowane.
        /// (np. jeśli n=1000, to branych będzie 512 próbek)
        /// </summary>
        public int SampleLimit
        {
            get { return _sampleLimit; }
            set { _sampleLimit = value; }
        }
        public EkgSignal ReadFile(string path)
        {
            EkgSignal signal = null;
            if (!File.Exists(path))
                throw new FileNotFoundException();
            using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
            using (StreamReader sr = new StreamReader(fs))
            {
                if (path.EndsWith(".txt"))
                {
                    Exception error = null;
                    try
                    {
                        signal = ReadTxtFile(sr);
                    }
                    catch (Exception e)
                    {
                        error = e;
                    }
                    finally
                    {
                        sr.Close();
                    }
                    if (error != null)
                        throw new IOException("Nie można wczytać pliku z sygnałem: '" + path + "'.", error);

                    else
                    {
                        string atrFilePath = Path.GetDirectoryName(path) + "\\" + Path.GetFileNameWithoutExtension(path) + "atr.txt";
                        if (File.Exists(atrFilePath))
                        {
                            using (var asr = new StreamReader(atrFilePath))
                            {
                                try
                                {
                                    ReadAtrFile(asr, signal);
                                }
                                catch (Exception e)
                                {
                                    error = e;
                                }
                                finally
                                {
                                    asr.Close();
                                }
                            }
                            if (error != null)
                                throw new IOException("Nie można wczytać pliku ATR: '" + atrFilePath + "'.", error);
                        }
                        else
                            throw new AtrFileNotFoundException("Nie znaleziono pliku ATR. Musi się on znajdować w tym samym folderze, co plik z sygnałem i mieć nazwę xatr.txt, gdzie x to nazwa pliku z sygnałem.");
                    }

                }
                else
                    throw new FileFormatException("Nieodpowiedni typ pliku.");
            }
            if (signal != null)
               signal.AdjustMaxMin();
            
            return signal;

        }
        EkgSignal ReadTxtFile(StreamReader sr)
        {
            EkgSignal signal = new EkgSignal();
            string linia = String.Empty;
            string[] slowa;
            
            int i = 0;
            while ((linia = sr.ReadLine()) != null)
            {
                if (i > _sampleLimit)
                    break;
                linia = linia.TrimStart();                
                slowa = linia.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                EkgPoint point = new EkgPoint { SampleNumber = int.Parse(slowa[0]), Value = int.Parse(slowa[1]) };
                signal.Add(point);
                i++;
            }
            int count = i;
            int part = (int)Math.Pow(2, Math.Floor(Math.Log(count, 2)));
            for (i = count - 1 ; i >= part; i--)
                signal.RemoveAt(i);
            
            return signal;
        }
        static void ReadAtrFile(StreamReader sr, EkgSignal signal)
        {
            string linia = String.Empty;
            string[] slowa;

            double prevTime = 0;
            int prevSample = 0;
            double deltaSum = 0;
            double delta;
            int atrCount = 0;
            while ((linia = sr.ReadLine()) != null)
            {
                linia = linia.TrimStart();
                slowa = linia.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                //1:232.434


                double time = ParseSpan(slowa[0]);
                int sample = int.Parse(slowa[1]);
                delta = (time - prevTime) / (sample - prevSample);
                deltaSum += delta;
                atrCount++;
                if (atrCount > 10)
                    break;

                var point = signal.Where(p => p.SampleNumber == sample).FirstOrDefault();
                if (point != null)
                    point.Time = time;
                else
                    break; // opisy w pliku ATR wybiegły poza opis sygnału.
                            // To nastąpi, kiedy została wczytana tylko część sygnału EKG z pliku .txt .
                prevTime = time;
                prevSample = sample;
            }
            double t = 0;
            delta = deltaSum / atrCount;
            for (int i = 0; i < signal.Count; i++)
			{
                signal[i].Time = t;
                t+=delta;
			}
        }
        static double ParseSpan(string span)
        {
            double x = 0;
            string expr = @"(\d):(\d\d\.\d\d\d)";
            var results = Regex.Match(span, expr);            
            x = 60 * int.Parse(results.Groups[1].Value) + double.Parse(results.Groups[2].Value, NumberFormatInfo.InvariantInfo);
            return x;
        }
            [Serializable]
    public class AtrFileNotFoundException : Exception
    {
      public AtrFileNotFoundException() { }
      public AtrFileNotFoundException( string message ) : base( message ) { }
      public AtrFileNotFoundException( string message, Exception inner ) : base( message, inner ) { }
      protected AtrFileNotFoundException( 
	    System.Runtime.Serialization.SerializationInfo info, 
	    System.Runtime.Serialization.StreamingContext context ) : base( info, context ) { }
    }
        }
}